#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct fraction
{
  int numerator;
  int denominator;
};

struct car
{
  char* name;
  float price;
  int speed;
};
// 匿名
struct 
{
  char title[500];
  char author[100];
  float value;
} b1;
// 直接赋值
struct 
{
  char title[500];
  char author[100];
  float value;
} b2 = {"Harry Potter", "J.K", 10.0},
  b3 = {"Cancer Ward", "Aleksandr Solzhenitsyn", 7.85};
// 起一个别名
typedef struct cell_phone
{
  int cell_no;
  float minutes_of_charge;
} phone;
phone p = {5551234, 5};
// 使用指针变量
struct book
{
  char title[500];
  char author[100];
  float value;
}* b4;

int main(void) {
  struct fraction f1;
  f1.numerator = 22;
  f1.denominator = 7;
  printf("%d\n", f1.numerator);
  struct car saturn = {
    "Saturn SL/2",
    16000.99,
    175
  };
  printf("%s\n", saturn.name);
  // 使用数组作为成员
  struct fraction numbers[1000];
  numbers[0].numerator = 22;
  numbers[0].denominator = 7;
  //struct 结构占用的存储空间，不是各个属性存储空间的总和，而是最大内存占用属性的存储空间的倍数，
  //其他属性会添加空位与之对齐。这样可以提高读写效率。
  struct foo {
    int a;
    char* b;
    char c;
  };
  printf("%ld\n", sizeof(struct foo)); // 24
  // 实际上的foo 
  struct foo1 {
    int a;        // 4
    char pad1[4]; // 填充4字节
    char *b;      // 8
    char c;       // 1
    char pad2[7]; // 填充7字节
  };
  // 定义 Struct 结构体时，可以采用存储空间递减的顺序，定义每个属性，这样就能节省一些空间
  struct foo3 {
    char c;
    int a;
    char* b;
  };
  // 结构体的复制
  struct cat { char name[30]; short age; } a100, b100;
  strcpy(a100.name, "Hula");
  a100.age = 3;
  b100 = a100;
  b100.name[0] = 'M';
  // 变量b是变量a的副本，两个变量的值是各自独立的
  //  struct 结构的属性必须定义成字符数组，才能复制数据   
  printf("%s\n", a100.name); // Hula
  printf("%s\n", b100.name); // Mula
  //赋值运算符（=）可以将 struct 结构每个属性的值，一模一样复制一份，拷贝给另一个 struct 变量。
  //这一点跟数组完全不同，使用赋值运算符复制数组，不会复制数据，只会共享地址。

  // 如果将 struct 变量传入函数，函数内部得到的是一个原始值的副本。
  struct turtle
  {
    char* name;
    char* species;
    int age;
  };
  void happy(struct turtle t) {
    t.age = t.age + 1;
  }
  struct turtle myTurtle = {"MyTurtle", "sea turtle", 99};
  // 值没有任何改变
  // 注意struct类型标识符并不是指针
  happy(myTurtle);
  printf("Age is %i\n", myTurtle.age); // 输出 99
  // 拿到结构本身
  void happy1(struct turtle* t) {
    (*t).age = (*t).age + 1;
  }
  // -> 箭头运算符
  void happy2(struct turtle* t) {
    t->age = t->age + 1;
  }
  // 三种等同的写法
  // t = &myTurtle
  // myTurtle.name == (*t).prop == t->prop
  // 嵌套
  struct species {
    char* name;
    int kinds;
  };
  struct fish {
    char* name;
    int age;
    struct species bread;
  };
  // 写法一
  struct fish shark1 = {"shark", 9, {"Selachimorpha", 500}};
  // 写法二
  struct species myBread = {
    "Selachimorpha", 
    500
  };
  struct fish shark2 = {
    "shark", 
    9,
    myBread
  };
  // 写法三
  struct fish shark3 = {
    .name="shark",
    .age=9,
    .bread={
      "Selachimorpha", 500
    }
  };
  // 写法四
  struct fish shark4 = {
    .name="shark",
    .age=9,
    .bread.name="Selachimorpha",
    .bread.kinds=500
  };
  // 自我引用
  struct node {
    int data;
    struct node* next;
  };
  // 定义一个链表
  struct node* head;
  // 列表 (11)->(22)->(33)
  head = malloc(sizeof(struct node));
  head->data = 11;
  head->next = malloc(sizeof(struct node));
  head->next->data = 22;
  head->next->next = malloc(sizeof(struct node));
  head->next->next->data = 33;
  head->next->next->next = NULL;
  // 遍历这个列表
  for (struct node *cur = head; cur != NULL; cur = cur->next) {
    printf("%d\n", cur->data);
  }
  // 位字段
  //实际存储的时候，C 语言会按照int类型占用的字节数，
  //存储一个位字段结构。如果有剩余的二进制位，可以使用未命名属性，填满那些位。
  struct {
    unsigned int ab:1;
    unsigned int cd:1;
    unsigned int ef:1;
    unsigned int gh:1;
  } synth;
  synth.ab = 0;
  synth.cd = 0;
  // 弹性数组成员
  struct vstring {
    int len;
    char chars[];
  };
  int n = 100;
  // 分配内存时确定
  struct vstring* str = malloc(sizeof(struct vstring) + n*sizeof(char));
  str->len = n;


  return 0;
}